#pragma once
#include <iostream>
#include <atomic>
#include <functional>

template<class T>
class Sharded_ptr
{
public:
	Sharded_ptr(T* ptr) :_ptr(ptr), _count(new int(1)) {}
	Sharded_ptr(Sharded_ptr<T>& sptr)
	{
		_ptr = sptr._ptr;
		_count = sptr._count;
		++(*_count);
	}

	Sharded_ptr& operator=(Sharded_ptr<T>& sptr)
	{
		if(_ptr != sptr._ptr)
		{
			delete* this;
			_ptr = sptr._ptr;
			_count = sptr._count;
			++(*_count);
		}

		return *this;
	}

	T* get() const 
	{
		return _ptr;
	}

	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}

	void Destory(Sharded_ptr<T>& sptr)
	{
		if (--(*sptr._count) == 0)
		{
			delete _ptr;
			delete _count;
		}
	}

	~Sharded_ptr()
	{
		Destory();
	}
private:
	std::atomic<int>* _count;
	T* _ptr;
};


template<class T>
class Unique_ptr
{
	using func_t = std::function<void(T*)>;
public:
	Unique_ptr(T* ptr):_ptr(ptr){}
	Unique_ptr(const Unique_ptr<T>&) = delete;
	Unique_ptr& operator=(const Unique_ptr<T>&) = delete;

	template<class D>
	Unique_ptr(T* ptr, D f) :_ptr(ptr), _del(f) {}

	T* get() const
	{
		return _ptr;
	}

	T& operator*()
	{
		return *_ptr;
	}

	T* operator->()
	{
		return _ptr;
	}


	~Unique_ptr()
	{
		_del(_ptr);
	}

private:
	T* _ptr;
	func_t _del = [](T* ptr) {delete ptr; };
};